ScalaMatsuri 2016 一日目 最速レポート【パートA】
ScalaMatsuri 2016 最速レポート パートA
こんにちは!今日は ScalaMatsuri 2016 にやってきました!
yad と二人で参加し、並列でたくさんのセッションを拝聴しました。僕がパートA、yad がパートBとして早速レポートします!
上記は ScalaMatsuri 2016 のA会場(400名) です。
目次
- Refactoring in Scala (中村 学さん)
- なぜリアクティブは重要か (岡本 雄太さん)
- リアクティブ・マイクロサービス (Christopher Huntさん)
- ランチタイムLT (複数名)
- バッチを Akka Stream で再実装したら 100 倍速くなった話 (根来 和輝さん)
- sbt-aws-ebプラグインを使って簡単デプロイ (かとじゅんさん)
- ScalaコードはJVMでどのように表現されているのか (阪田 浩一さん)
- 実用関数型アーキテクチャのパターン (Raul Rajaさん)
- Playソースコード完全マスターへの道 (高橋 俊幸さん)
- ドワンゴアカウントシステムを支えるScala技術 (結城 清太郎さん)
Refactoring in Scala
発表者: 中村 学さん
スライド
ピックアップワード
- 値の型とAnyValの継承
- IsoとPrism
レポート
- Value Types(値の型)について。DDDでは値オブジェクトと呼ばれるものについて。
- エンティティのIDがLongだと、うっかり
Map[Long, User]
の apply などでうっかりミスが発生しうる。 - Type Alias を使って
type UserId = Long
などとすることも可能。Map[UserId, User]
は Clear semantics.type UserId = String
のリファクタも楽ちん。- でも(3つ前の)apply のミスは依然としてコンパイラに検知されない。
- Tagged Type を使う。Tagged は
type Tagged[A, T] = {type Tag = A; type Self = T }
- Tagged には @@ という Alias を貼る。
- すると
Map[Id @@ User, User]
のようなことは可能、userMap.apply(board.id)
は型エラーに。 - コンパイルに最適化されるキャストのため、Anyval出ないかぎりは wrap / unwrap のコストがない。
- Value Class
case class UserId(value) extends Anyval
を使う
- Value Class
- AnyVal継承せよ! wrap / unwrap のコストを回避しよう。
- いくらかの特別な場合にはメモリ割り当てが行われるっことがあります。
- Tagged Type Pros: Container type cast support
- Value Class Pros: メソッドをはやせる
- Value Class Cons: 煩雑
- Phantom Type による ID の表現
case class Id[A](value: Long) extends AnyVal
->Id[User]
case class Name[A](value: String) extends AnyVal
->Name[Board]
- コンパイルすると inline されるため幽霊のように消える。つまりそれがファントムということ。
- エンティティのIDがLongだと、うっかり
- 境界における型変換
- RDBのDTOなどとの境界には独自型との型変換が発生する。これは避けられない。
- Play や skinny なら formatter の用意があり、それに乗っかるのがよい。
- でも全ての型に提供するのは煩雑。
- Isomorphism を使う
to(from(a)) == a
かつfrom(to(b)) == b
が Iso。
- Prism を使う
to
がtoOpt
になった Iso
- Iso は Prism になれる
override def toOpt = Some(value)
- Monocle や shapeless といったライブラリを使えば自分で定義しなくてもよい。
- Iso や Prismやアーキテクチャに依存しない。ので、これを継承した値型がドメイン層の責務を破ることはない。
- それでも煩雑ではあるので、implicit macroを使っていく……? → macro は experimental なので使わない選択肢も。
- RDBのDTOなどとの境界には独自型との型変換が発生する。これは避けられない。
質疑応答
- abstract type member を使えば隠蔽が可能かもしれない?
- やるとすれば、隠蔽の箇所をどこにするか、などを考慮していく必要があります。
なぜリアクティブは重要か
発表者: 岡本 雄太さん
スライド
ピックアップワード
- Reactive Systems
レポート
- リアクティブプログラミング
- 解決したい問題: 非同期, 並列, Fault Toleranceなど
- 全部リアクティブで解決しよう!みたいな風潮がある気がする。いろいろあってよくわからない人も多いかと。
- GUI におけるリアクティブ
- GUIイベントをイベントストリームとして考えましょうというリアクティブ
- NetWorkにおけるリアクティブ
- 2つのマイクロサービスへの並行処理をリアクティブ
- ビッグデータ処理などの並行処理をリアクティブ
- Java9 には ReactiveStream が!
- リアクティブいろいろにもある。
- Foundations of Reactive
- Reactive Component
- Reactive DataFlow のふたつ
- Reactive Component
- in と out があるコンポネント。inに値が来た時だけ反応(react)する
- Actor でも Function でもなんでもいい
- Reactive DataFlow
- 連結によるリアクティブの流れ
- 非同期と並列をらくにしたい問題を解決
- callbackではだめなの?
- callback はモジュリティが低いから。再利用しにくい。
- 状態がある。
- 実行順序がよくわからない。
- コールバック地獄。破滅のピラミッド Piyramid of Doom
- レキシカルスコープによる暗黙の参照関係こそがステート。
- Reactive component だとどうなるの
- in と out のみ。自己完結性があり、すべての依存関係 in に集まる。モジュール化可能。
- 独立したライフサイクルをもつ。状態や障害はその中にふさぎこまれる。
- でも 実行順序がよくわからない はまだ解決できていない。
- callbackではだめなの?
- そこで Reactive Programming
- Reactive DataFlow で実行順序が定まっている。
- 普段ぼくらが使っているのは命令形なので、このFlowを直感的に想像できる人は少ないだろう。
- じつに驚くべきことは、命令形のプログラミングはReactive DataFlowにそのまま転写かのうなのである。
- 脚註: a binary tree of operators
- DataFlowでは実行順序は担保されないが、それでよいのだ
- さまざまな実行順序が考えられる。ある in がかわったら、全体の out にも影響する。
- 具体例
- 脚註: 同氏のスライド参照のこと。
- なんで RP の話だとすぐ FP がでてくるのか。
- FP は モジュール化のサポートが強いからだ!
- 遅延評価と高階関数
- Lazy は、非同期イベント生成器としてのモデルを持っている(脚註: Enumratorに近い?)
- Higheroder funciton は問題解決のモデルを持っている(脚注: Iterateeに近い?)
- FP は モジュール化のサポートが強いからだ!
- 非同期の文脈と、我々のプログラムを分離する。
- 上記らによって、実行モデル(DataFlowを定義する部分)と問題解決の部分を分離することができる。
- How = DataFlow
- What = DSL(脚注:我々のプログラムのこと)
- RP's Pros
- ポータビリティ
- リアクティブなプログラムはいろんなアーキテクチャに投影可能だ(JavaVMやマシンにとどまることではない!)
- 最適化
- ランタイムでデータフローを最適化できる!(分散処理などの)
- すでに Akka Streams 2.0 で Fusing が入る予定で、現実の話になってきてきます。
- DataFlow 実例
- Hadoop以降の学習/ビックデータ周辺やテンソルフローもこのモデルを採用してるよ。
- ポータビリティ
- Architecture
- シングルマシンから分散システムへ!
- スケーラビリティと耐障害性の観点から、分散システムにしようというビッグウェーブ
- このアーキテクチャの考え方は Reactive System という (TypeSafe社の提案でもある)
- すでに Twitter などで採用されている問題解決のモデルに名前をつけたもの
- Architecture 実例
- マイクロサービスアーキテクチャ
- マイクロサービスが Component であり、全体が DataFlow なのだ。
- 脚注: AWS Lambda Function も Component ですね。
- Bigdata だけでなく WebService にもリアクティブ
- Dockerfile によって状態を管理しようとしているのに、はたしてそれが Immutable でしょうか。
- いずれ DataFlow は WebService にも適用可能になっていくでしょう。
リアクティブ・マイクロサービス
発表者: Christopher Huntさん
ピックアップワード
- Reactive Manifest
- Resiliency and Elasticity
レポート
- (駐: 実践的デモがメインのセッションでした。随所でPlayFrameworkとConsiderRをつかってデモを行います)
- Reactive において何か重要なのか?
- Resiliency である。これが第一であり、これがなければ意味が無いと言っていい。 (脚注: 耐障害性)
- アンケートの結果 Elasticity が重要だとみな言う(脚注: スケーラビリティ)。が、実際は耐障害性がより重要である。
- 銀行のシステムが半日ダウンした、これを想像できるだろうか。
- Play でこれをどのように行うか
- Controller に DI された CustomerService について考える。
- その実態は Akka の Actor になっている。
- context.become でキャッシュ戦略を実装している。
- さて CustomerService は Actor であり Component だ。
- おめでとう、マイクロサービルの完成だ! (脚注: 反語表現)
- これ(上記であげたマイクロサービスのようなもの)はリアクティブではない!
- 永続化層が死んだらどうする?
- これはどうスケールアウトできる?
- 複数インスタンスのキャッシュ戦略は?
- これは明確にリアクティブではない!
- サービス(ここでは Actor)間の連携はどうする?
- MQ
- システムをリアクティブにするにはどうしたら良いのか?
- データストアのクラスタ化を前提とする。そもそも、シングルインスタンスのDBを考えない。
- つねに複数ノードでの実行を考慮する。
- ConductR を使って下さい!
- シグナルをクラスタに送ること (Akka Cluster)
- ConductR の説明
- アプリのロジックについて考えるのはプロジェクト全体の10%程度だろう
- ほか(90%)はテストとDevOpsについて考えているはずだ
- 本番稼働するまで、全体像はわからないのです!(IPのCIDRだって、本倍デプロイまで定まらないでしょう?)
- ConductR はクラスタ化されたリアクティブアプリケーション(マイクロサービスのような)のデプロイと管理行う。
- 目指すものは、Actor System の延長です。インスタンス(ノード)間の Actor System の境界を、取り払うのです。
- ConductR Visualizer
- 今、各クラスタの状況がどのようになっているか、を ConductR の Visualizer によって WebUI から確認可能です
- 脚注: アニメーション化された、SPAのようなインターフェイスで、各ノードの処理状況を確認できる
- 各クラスタに Customer のマイクロサービスを追加してみましょう。(脚注: リアルタイムで追加され、VisualizerのUIにもすぐ反映されました)
- クラスタ化されたアプリケーションへは
$CONDUCTOR_IP
でアクセス可能です。 - つまり、例えばカスタマーのリストAPIは
GET http://$CONDUCTOR_IP/customers
でテスト可能なわけです。 - Play アプリケーションを ConductR に be compatible は簡単です。 2つのsbt-modulesとlibraryDependencyを追加するだけ。
- Play だけじゃない。ほかのアプリケーション(Akkaベース)だって、DBだって管理します。
- 今、各クラスタの状況がどのようになっているか、を ConductR の Visualizer によって WebUI から確認可能です
- 覚えておいて下さい
- ノードを立ち上げるのではない、クラスタを立ち上げます。
ランチタイムLT
ランチタイムには、速いテンポで続々とLTがありました(すみません、お名前やタイトルをメモしきれませんでした。今回は一括で省略させていただきます) ScalaJSやgRPC(ScalaPB)、耐障害性についてなどのセッションがありました。
中でも印象的だったのが、「Dwangoさんの研修用プライベートgithubリポジトリをリアルタイムで公開する」というLTでした!
公開されたリポジトリはこちら。
pull req ベースでScalaドキュメント作成に参加できる?ようです(間違っていたらすみません!)
バッチを Akka Stream で再実装したら 100 倍速くなった話
発表者: 根来 和輝さん 15分セッション
スライド
ピックアップワード
- Akka Streams
- 並列処理と成果物の順序性
レポート
- シンプルな夜間バッチがあった
- Rails の runner で実装
- DB から情報を一括で取得して、一行ずつ処理しCSVを出力する普通のバッチ
- 処理がとても遅かった。 明日の朝には終わっていてほしいので、応急処置(インスタンスサイズや並列化)でしのいでる。
- なぜこのバッチが遅いのか?調査
- 本番と同等の調査環境を立ち上げ
- 100万レコードを挿入する調査したところ、4件/1sec処理できていた。
- 100万件終わるのに3日間かかる。
- ボトルネックの調査 - 結論、一括でDBから読み込む場所のメモリが足りてないのが主因だった。(スワップしてる)
- ということでメモリを 2GB → 8GB に変えたら、 100件/1秒 でできた。
- これで3時間で終わるようになった、スワップしなくなったため。
- でも正直まだまだ遅い、追って調査するとCPUが新たなボトルネックになっていることが判明。
- ということで、このバッチの構造を並列化することにした。
- 並列化
- 成果物のCSVは 時刻順に整列されているひつようがある 。
- バッチ処理を並列化しただけでは、成果物の順序がこのようにはならない。
- そこで Akka Streams をつかったのです。
- Akka Streams とは
- Reactive Streams の Akka を使った実装。
- アーキは
- DB -> Rails(Active Record) -> CSV から
- DB -> Slick -> AkkaStreams -> CSV にした!
- AkkaStreams では、処理自体は並列で行われる(CPUをフル活用できる)もののinとoutは一元化されるので順序の問題を回避できる
- 実装例
- mapAsyncメソッドでFlow(処理)のみを並列実行する
- Sinkにつなぐ結果の順序性は保たれる
- ……という実装にしたら 1,180件/1秒になった → 14分に短縮!
- しかもメモリは2GBのまま
- ちなみにDBのキャッシュを実装したら100倍じゃなくて295倍でした。なので実際は295倍速くなったんです。
sbt-aws-ebプラグインを使って簡単デプロイ
発表者: かとじゅんさん 15分セッション
Github Repository
ピックアップワード
- Elastic Beanstalk
レポート
- sbt-aws をフル活用したい。
- sbt-aws-eb プラグインを作りました。
- ElasticBeanstalk の環境構築からデプロイまでの煩雑な手続きを自動化しています。
- 利用するには sbt-assembly と sbt-aws-eb を依存解決に追加します。
- もちろん ebextentions にも対応しています
- aws::ebDeploy タスクさえ実行すれば、必要な下位のタスクを推理して実行してくれます。
- (感想) これはすごく便利!うちでもぜひ使いたいです。
ScalaコードはJVMでどのように表現されているのか
発表者: 阪田 浩一さん 15分セッション
スライド
ピックアップワード
- javap
レポート
- JVM 大好きです。
- Scala コードをコンパイルすると何が起こりますか?
- クラスファイルになります。
- そして、JVMがクラスファイルを実行コードにします。
- みなさん、クラスファイル開いたことありますか?
- まず Java をコンパイルしてみて、クラスファイルについて見ると
- HelloWorld.java をコンパイルすると HelloWorld.class ができます
- クラスファイルの定義はバイト毎に区切られています。
- クラスファイルは必ず 0xCAFEBABE から始まります!
- 続いてメジャーバージョン、マイナーバージョンと続きます……
- これでクラスファイルが読めるようになりました。(脚註: 反語表現)
- javap command
- javap コマンドでクラスファイルの逆コンパイルができます。
- メソッド定義や、コンスタントプールなどがコメント付きで読めます。
- JVMの命令は200個、そのうち
invokedynamic
を除いた199個は最初からあったんです。- このことから、JVMの初期設計がいかに優秀だったかがわかります。
- 次に Scala コードをコンパイルし、クラスファイルについて見ると
- 例にあげた HelloWorld.scala をコンパイルすると HelloWorld.class と HelloWorld$.class のふたつができます
- クラスファイルの中はJavaのクラスファイルと似ていますね
- 次に Scala トレイトをコンパイルしてみると
- Similarity.scala をコンパイルすると Similarity.class と Similarity$class.class
- トレイトはJavaのInterface相当とabstract class相当の2クラスファイルにわかれました。
- また、Scala のパターンマッチをコンパイルしてみると、Javaのifやswitchに相当するコードになります。
- カリー化では、無名関数ごとにクラスファイルが分かれているのがわかります。
- 上記などから Scala コンパイラが少ないコードから多くのロジックを生成していることがわかります。
javap
を是非使ってみてください。
実用関数型アーキテクチャのパターン
発表者: Raul Rajaさん
Github Repository
ピックアップワード
- Free of Interpretation: Free Modnads
- Support Parallel Computation: Free Applicatives
- Compsable pieces: Coproducts
- Dependency Injection / Inversion of Control: Implicits & Kleisli
- Fault Tolerance
- Cats
レポート
- (脚註: 筆者(私)の圏論レベルは1以下のため、誤ったレポートをしている可能性が存分にあります。どうかご容赦下さい。)
- 解釈の自由: Free モナド
- 構造に対して値を与える事ができます。
- ある
Interact[A]
代数的データ型Ask a | Tell a
を持っています
- Application はこれらによって定義される代数の積であるといえます。
- CatsApp の Interact と DataSource を定義し、それがDIされてビジネスロジックが完成するまでをデモしています。
- 脚註: デモ中のコードについては同氏のスライド参照
- ここまでで Application の構造を定義しました。しかし、まだ実行することはできません。
- Application はそれぞれの問い合わせに対応する定義が (Interpreterとして) 必要です。
- Interpreter は `(A ~> Task) のような型をもちます。
- さて Application は代数の積であると言いました。アプリケーションも同じです。
val interpreters: CatsApp -> Task = SomeInterpreter or AnotherInterpreter
- 最後の居 Task のモナド型クラスのインスタンスにします。
- これでついに program を実行可能になりました。
- アプリケーションの芯部を構造から分離可能になったといえます。
- 解釈: Free アプリカティブ
- 並列な実行を(アプリカティブは)サポートするでしょう。
- 先ほどのように、処理を代数として定義しましょう。ここでは、Validationの処理を代数定義します。
- また、先ほどのように、 FreeApplicative の Validation を定義します。
- 脚註: デモ Kleisli を利用しての定義をすすめる。
- Validation の処理は Applicative Builder として構築されます。例えば、2つのバリデーションを行えば、その2つの値が帰るまで結果処理を行いません。この2つの処理が並列であることは、Interpreterが保証する部分になるでしょう。
- 最後に、先ほどのように Interpreter の積をとり、それが Application です。
- 集約可能性
- cats.data.Kleisli はモナド的な関数の合成を行う側面があります。
- 依存性の注入と制御の反転 - Implicit や Kleisli を使うことができます。
- 耐障害性
- Future や Try の中で例外をスローさせます。そしてそれは唯一(例えばfutureであればrecover)の対応する処理が必要です。
- あなたが例外について、わすれてしまったとしましょう。残念なコトになるのがわかりますね。
- Future にしろ Try にしろ Xor にしろ、例外の情報は失われます。(Exceptionの種類が2種類以上あるのを想像してください。
Xor
や z の\/
にちかいものを使って解決しましょう。rapture.core.Resultです。- 結果をまず言うと、次のようなものがタイプシグニチャされるでしょう
rapture.core.Result[Int, NumberFormatException with IllegalArgumentException]
- Int は PositiveCase です
- タイプシグニチャとして、例外の情報が蓄積されるのがわかるでしょうか?
- これを future の recover のようなものを行うにはどうするのでしょうか。
- reconcile を使って、別の例外にもできますし、recover もできます。
- この時、異常系の型情報が失われていないのです!
- 次に知るべきもの
- Free だけでは足りないことも有ります。興味がある場合は、Scalaのコミュニティ
質疑応答
- プレゼンツールは何をつかっていますか?(脚註: なんとこのプレゼン、CLIで行われましたw)
- represent(聞き取れず)というものです。Markdownみたいなスライドを書くことができます。
- プレゼン資料に含めたコードをSBTコンソールで実行することもできます
- 今話しにあった概念を現実のプロジェクトに使えるんでしょうか?
- 幾つかオープンソースの実績があります。あと、ScalaExercisesというのが弊社にはあります。
- Github repository
Playソースコード完全マスターへの道
発表者: 高橋俊幸さん
ピックアップワード
- Play Framework
- 依存性の注入
- 型クラス
レポート
- Play のソースを読むのは意外と簡単です、ということをお伝えしたいです。
- ソースコードを読むタイミング
- 何かバグってない?というとき。
- フレームワークのプラグインを作りたいな、と思った時や、プラグインを参考にしたくなったとき。
- 勉強したいな、と思った時。
- 暇つぶしにも。
- ソースコード難しい、と思う理由
- 10万行オーダーのソースコード、どんっとでてきてもわからない。
- あるif文を見たところで全体がわからないから何のことかわからない。
- 木を見るより森を見たい。
- アジェンダ
- buiud.sbt を読む
- パターンを知る(型クラス等)
- モジュールを知る
- マスターしよう
- Play の build.sbt
- libraryDependencies を読めば、何に依存しているのか、何をやっているのかがおおまかにわかる。
- 例えばJSONを扱っているとか、HTTP通信を行うとか。
- 例えば、JSON周りで不正があったとき、そのライブラリがバグってるのか、そのライブラリの依存ライブラリがバグってるのか判断できるようにする。
- dependsOn はビルド依存関係を示す。これはコードを読む上で道標になる。
- libraryDependencies を読めば、何に依存しているのか、何をやっているのかがおおまかにわかる。
- Play の歴史
- リファクタリングとモジュール化の歴史でした
- かつて play-java と play-scala が同一のjarであったけれど、これがわかれ、play-scalaは身軽になった。
- モジュール化が進み、別のリポジトリに分かれたり、あるいは別のライブラリに代替されたりしています。 - 今は依存関係やモジュールがわかりやすいので、前に比べれば気軽によめます!
- Play のパターンを知る
- ひとつめは Dependency Indjection
- ふたつめは型クラスとしての Implicit Parameter
- Dependency Injection
- なにでDIしましょうか。(Reader, Cake Pattern, Assembler, Implicit etc.)
- Play 2.4 は Constructor Based Injection です。
- なぜ DI するのか、というと、モジュールを細かく分けたかったから。
- play.api.Play.current をもう使いたくない!
- なので Runtime DI と Compile time DI をサポートしてます。
- 基本は Runtime DI。どうしてもって人だけ Complile time DI を(自前実装で)使う。
- DI によって単体テスト可能に。
- Runtime DI
- Guice の Module の定義に従う。
- Compile time DI
- Cake Pattern に近い
- Play のモジュールは(ほぼすべて)この Runtime DI の手法に従う。ので、この部分が読めると、あとはロジックが残る。
- ここを乗り越えれば読めるようになる!
- 型クラス
- Implicit parameter を利用する
- Java だと、拡張したいなという時には継承して機能を追加します。
- この時 open close principle にそぐうかなど、ややこしい規則があるが、型クラスはそうでもない。
- ある既存の型 A を enrich する B(a) というクラスを作り、それを implicit として受け入れれば
- アドホック多相性を実現できる。
- 例
- Writable
- Ok とか BadRequest とか!
- ContentTypeOf
- QueryStringBindable
- PahtBindable
- etc
- Writable
- モジュールを知る
- play モジュール
- Play のコアの部分
- Action, Routing, Configuration, など
- play-java モジュール
- 上記の java 版
- Play についての多くが play に含まれるので、 play を読むことがかなり多いです。
- play-jdbc モジュール
- これを使ってかってに作ってくれ!というたぐいのもの。
- 強いられているわけではない。play-slick でも play-ebean でもOK。
- ws
- AsyncHttpClientのラッパー
- play-cache
- キャッシュのモジュール
- play-json
- play の DI の仕組みにのっていない
- 完全に別のライブラリとして開発
- Play 3.0 で Json4s に変わる……?
- play-server
- Playのserver実装。Nettyとかakka-httpとか。
- play-stream
- Reactive Stream の Play 実装
- sbt-plugin
- assetのコンパイルなど、開発環境についての知識が詰まったモジュール。
- SBTの仕組みにのっかっている。さっきのDIの仕組みではないです。
- play モジュール
ドワンゴアカウントシステムを支えるScala技術
発表者: 結城 清太郎さん
スライド
ピックアップワード
- 継続モナド
- Cake Pattern
レポート
- 巨大な Web アプリケーションをコンポーネント化し、再利用していきたいと考える。
- シンプルで表現力の高いコンポーネント化技術が必要になる。
- この技術として、継続モナドを用いている。
- 典型的な Web アプリケーションは処理に深いネスト構造を持っている。
- 認証処理にを行うアプリケーションのシーケンスを考える。nested で分岐の多いシーケンスになる。
- 継続を明示的に受け渡しする = 継続渡しスタイル
- この継続渡しを型クラスとして表現したのが継続モナド。
- 継続モナド
Cont (A -> R) R
- Cont はモナドであるから、bind できる。これが Play の Action と比べて大きな利点になる。
- bind できるから、処理を自由に合成(脚註: 糊付け)できる。
- (PlayのActionFunctionも、継続の概念を持っている。しかし、モナドではないから、合成ができない。)
- 23万行の膨大なコード
- モジュール化して、DIで管理する。
- Guice
- Java での実績から安心して使える。
- コンパイル時間の短縮に繋がる。
- 学習コストがかかる、実行時エラーが起こる可能性がある。
- Cake Pattern
- 厳格で静的な型チェック
- DIコンテナつきのフレームワーク以外でも利用可能
- Readerモナド
- 関数型界隈で推されている
- 複雑さ、影響範囲などの理由から、DIに向いていない
- Cake Pattern の再考
- 既存の Cake Pattern は複雑
- 本質のみに注視した Minimal Cake Pattern の提唱
- Userドメインにおける具体例について
- 脚註: コードでの具体例を示されています。スライドをご参照ください。
- DI だけに特化した Minimal Cake Pattern を再評価する
- 型エラーをコンパイル時に判定可能なことは、巨大なアプリケーションコードにおいて明確に利点である
- ストレージアクセスの抽象化
- ストレージアクセスの抽象化する。
- これを Fujitask。Fujitask型はモナド。
- Fujitask の構造
- Task[-A, +B] trait
- 脚註 標準的な in / out の反, 共定義に見える cf. Function1
- TaskRunner[A] trait
- Transaction
- ReadTransaction extends Transaction
- ReadWriteTransaction extends ReadTransaction (超重要)
- Task[-A, +B] trait
- Fujitask はバックエンドDBに依存するような抽象型ではなく、DBアクセスそのものを抽象化している(と読み取りました、間違ってたらすみません)
- Fujitask の
Task[Transaction, T]
らは bind できるし、たとえばTask[ReadTransaction, _]
とTask[ReadWriteTransaction, _]
の糊付けをすると、全体の文脈はTask[ReadWriteTransaction, _]
になる。これは ReadWriteTransaction extends ReadTransaction だからです。 - 最終的な型の判定で全体が ReadTransacation なのか? ReadWriteTransaction なのかがわかります。
- ここまで紹介した技術は、先進的な技術を用いていますが、どれもシンプルです。必要最低限のことしか行いません。
質疑応答
- Fujitask の Transaction について、より複雑な場合にはどうしますか。
- Fujitask が必要十分だから、現在はこれがよいと思います。(型クラスなどで)拡張するのはよいアイデアです。
- 継続モナドによってコンポーザビリティを確保していますが、このFutureモナドで代用するのはどうか?(同期的なタスクでもFutureをつかう、などで)
- 同期的な Future も使っています。継続モナドが必要になる処理のうちの8割くらいは Future で代用可能です。ただ、たとえは、リクエストに応じてレスポンスを変更するとか(※聞き取りミスの可能性)などの例外的ケースに対応するために、継続モナドを採用しています。
まとめ
非常に充実した、また実践的な、そしてちょっと高度な(抽象度の高い)セッションが目白押しでした。とても楽しめました!
これから懇親会に参加してきます!ではまた!